//
//  ZPlatform.h
//  Part of Z-Way.C library
//
//  Created by Alex Skalozub on 1/6/12.
//  Based on Z-Way source code written by Christian Paetz and Poltorak Serguei
//
//  Copyright (c) 2012 Z-Wave.Me
//  All rights reserved
//  info@z-wave.me
//
//  This source file is subject to the terms and conditions of the
//  Z-Wave.Me Software License Agreement which restricts the manner
//  in which it may be used.
//

#ifndef zplatform_h
#define zplatform_h

#define Z_STATIC_STRLEN(s) ((sizeof(s) / sizeof(s[0])) - sizeof(s[0]))
#define Z_SIZE_MEMORY_FOR_PRINTF_NUMBER(NUMBER) ((((sizeof(NUMBER) * __CHAR_BIT__) / 3) + 2) * sizeof(ZWCHAR)) // The right formula for the number of decimal digits is: bits_num / log2(10), +2 for rounding up and \0

#define Z_SIZE_FIELD_STRUCT(structure, field) (sizeof(((structure *)NULL)->field))

// Buffer for copying from one file to another and some other files operations requiring intermediate buffer. The size is selected to be optimal for each type of system
#if defined(__ESP32__)
    #define Z_PLATFORM_SIZE_MEMORY_FOR_FILES_OPERATION 128
#else
    #define Z_PLATFORM_SIZE_MEMORY_FOR_FILES_OPERATION 1024
#endif

#if Z_PLATFORM_SIZE_MEMORY_FOR_FILES_OPERATION % 2 != 0
    #error "Z_PLATFORM_SIZE_MEMORY_FOR_FILES_OPERATION must be a multiple of 2!!!"
#endif

#if defined(__ESP32__)
    #define __ZWAY_PLATFORM_SMALL_SPACE_DISK__
    #define Z_PLATFORM_MEMORY_BLOCK_SIZE 32
    #define Z_PLATFORM_LOW_PERFORMANCE
#else
    #define Z_PLATFORM_MEMORY_BLOCK_SIZE 1024
#endif

#if Z_PLATFORM_MEMORY_BLOCK_SIZE % 2 != 0
    #error "Z_PLATFORM_MEMORY_BLOCK_SIZE must be a multiple of 2!!!"
#endif

#if !defined(__ZWAY_SAVE_XML__) && !defined(__ZWAY_SAVE_CBOR__)
    #define __ZWAY_SAVE_XML__
    #define __ZWAY_SAVE_CBOR__
#endif

#if !defined(__ZWAY_SAVE_ONE_FILE__) && !defined(__ZWAY_SAVE_SEPARATE_DEVICE_FILES__)
    #define __ZWAY_SAVE_ONE_FILE__
    #define __ZWAY_SAVE_SEPARATE_DEVICE_FILES__
#endif

#if !defined(__ZWAY_ZDDX_DB__)
    #define __ZWAY_ZDDX_DB__ 1
#endif

#if !defined(__ZWAY_STORAGE_VIA_DH__) && !defined(__ZWAY_STORAGE_VIA_FILE__)
    #define __ZWAY_STORAGE_VIA_DH__
#endif

#include "ZErrors.h"

#if defined(__linux__) && ! defined(_GNU_SOURCE)
#define _GNU_SOURCE
// this involves __USE_UNIX98 and __USE_GNU
// used for pthread_setname_np, vasprintf, PTHREAD_MUTEX_RECURSIVE and PTHREAD_MUTEX_RECURSIVE_NP
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>

#ifdef _WINDOWS
	// disable unknown pragma warnings
	#pragma warning(disable: 4068)
    #define TODO(x) __pragma(message("TODO: " #x))
#else
    #ifdef __GNUC__
        #pragma GCC diagnostic ignored "-Wunknown-pragmas"
    #endif
    #define DO_PRAGMA(x) _Pragma (#x)
    #define TODO(x) DO_PRAGMA(message("TODO: " #x))
#endif
          
#ifdef _WINDOWS
    #define ZWINLINE __inline
    #ifdef __cplusplus
        #define ZWEXPORT_PRIVATE extern "C"
        #if defined(ZWAY_STATIC)
            #define ZWEXPORT extern "C"
        #elif defined(ZWAY_EXPORTS)
            #define ZWEXPORT extern "C" __declspec(dllexport)
        #else
            #define ZWEXPORT extern "C" __declspec(dllimport)
        #endif
    #else
        #define ZWEXPORT_PRIVATE extern
        #if defined(ZWAY_STATIC)
            #define ZWEXPORT
        #elif defined(ZWAY_EXPORTS)
            #define ZWEXPORT __declspec(dllexport)
        #else
            #define ZWEXPORT __declspec(dllimport)
        #endif
    #endif
#else
    #define ZWINLINE inline
    #ifdef __cplusplus
        #define ZWEXPORT_PRIVATE extern "C" __attribute((visibility("default")))
        #define ZWEXPORT extern "C" __attribute((visibility("default")))
    #else
        #define ZWEXPORT_PRIVATE __attribute((visibility("default")))
        #define ZWEXPORT __attribute((visibility("default")))
    #endif
#endif

// wrapper for unused variables
#ifdef __GNUC__
#define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#define UNUSED_FUNC(x) __attribute__((__unused__)) UNUSED_ ## x
#ifdef DEBUG
    #define RELEASE_UNUSED(x) x
#else
    #define RELEASE_UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#endif
#else
#define UNUSED(x) UNUSED_ ## x
#define UNUSED_FUNC(x) UNUSED_ ## x
#ifdef DEBUG
    #define RELEASE_UNUSED(x) x
#else
    #define RELEASE_UNUSED(x) UNUSED_ ## x
#endif
#endif

#define UNUSE(x) (void)(x)

// optional __printflike() specifier (unavailable on some platforms)
#ifndef __printflike
	#ifdef __GNUC__
		#define __printflike(fmtarg, firstvararg) __attribute__((format(printf, fmtarg, firstvararg)))
	#else
		#define __printflike(fmtarg, firstvararg) 
	#endif

	#if defined(_WINDOWS) && _MSC_VER >= 1400
		#include <sal.h>
		#if _MSC_VER > 1400
			#define PRINTF_FORMAT_STRING _Printf_format_string_
		#else
			#define PRINTF_FORMAT_STRING __format_string
		#endif
	#else
		#define PRINTF_FORMAT_STRING 
	#endif

#else
	// __printflike defined, no need for other checks
	#define PRINTF_FORMAT_STRING
#endif

// for fall through (no break)
#if !defined(__fallthrough)
	#if defined(__has_cpp_attribute) && __cplusplus >= 201703L
		#if __has_cpp_attribute(fallthrough)
			#define __fallthrough [[fallthrough]]
		#elif __has_cpp_attribute(gnu::fallthrough)
			#define __fallthrough [[gnu::fallthrough]]
		#endif
	#elif defined(__has_attribute)
		#if __has_attribute(__fallthrough__)
			#define __fallthrough __attribute__((__fallthrough__))
		#else
			#define __fallthrough
		#endif
	#else
		#define __fallthrough
	#endif
#endif

#ifdef _WINDOWS
#include <WinSock2.h>
#include <Windows.h>
#include <shlwapi.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <time.h>
#include <tchar.h>

// suppress warnings about deprecated POSIX function names
#pragma warning(disable: 4996)

// suppress warnings about non-dll interface as base class
#pragma warning(disable: 4275)

TODO(Why unsigned here and signed on *nix platforms?)
typedef BYTE ZWBYTE;
typedef uint16_t ZWWORD;
typedef uint32_t ZWDWORD;
typedef uint64_t ZWDWORD64;
typedef unsigned char ZWBOOL;
typedef int64_t ZWLONGINT64;

typedef TCHAR ZWCHAR;
typedef LPTSTR ZWSTR;
typedef LPCTSTR ZWCSTR;

typedef HANDLE ZW_FILE_HANDLE;

typedef DWORD speed_t;
typedef unsigned short mode_t;

#define copy_str(s) _tcsdup(s)
#define compare_str(s1, s2) _tcscmp(s1, s2)
#define compare_str_ci(s1, s2) _tcsicmp(s1, s2)
#define str_length(s) _tcslen(s)
#define strcasecmp(s1, s2) _stricmp(s1, s2)
#define strncasecmp(s1, s2, maxCount) _strnicmp(s1, s2, maxCount)


#define ZSTR(s) TEXT(s)

// implementation of UNIX-only functions:
#define snprintf(buffer, size, format, ...) _snprintf(buffer, size, format, __VA_ARGS__)

#define sleep(sec) Sleep((sec) * 1000)
#define sleep_ms(ms) Sleep(ms)

struct timezone 
{
	int tz_minuteswest; /* minutes W of Greenwich */
	int tz_dsttime;     /* type of dst correction */
};

ZWEXPORT_PRIVATE int gettimeofday(struct timeval *tv, struct timezone *tz);

#define O_NOCTTY 0

ZWEXPORT int fsync(int fd);

#else

#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
#define __sd_linux_use_directIO__ 1
#include <sys/io.h>
#endif

#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>

#ifdef __MUSL__
#include <errno.h>
#else
#include <sys/errno.h>
#endif

#include <sys/stat.h>
#include <sys/time.h>
#include <termios.h>
#include <memory.h>

#define SOCKET_ERROR -1
#define closesocket(x) close(x)
#define _sleep(x) usleep((x) * 1000)

#ifndef O_BINARY
#define O_BINARY 0
#endif

typedef unsigned char ZWBYTE;
typedef unsigned short int ZWWORD;
typedef unsigned int ZWDWORD;
typedef unsigned long long int ZWDWORD64;
typedef unsigned char ZWBOOL;
typedef long long int ZWLONGINT64;

typedef char ZWCHAR;
typedef char* ZWSTR;
typedef const char* ZWCSTR;

typedef int ZW_FILE_HANDLE;
typedef int SOCKET;


#define copy_str(s) strdup(s)
#define compare_str(s1, s2) strcmp(s1, s2)
#define compare_str_ci(s1, s2) stricmp(s1, s2)
#define str_length(s) strlen(s)

#define ZSTR(s) s

#ifdef __FREERTOS__
#define sleep_ms(ms) vTaskDelay(ms/portTICK_PERIOD_MS);
#define sleep(s)     sleep_ms((s) * 1000)
#else
#define sleep_ms(ms) usleep((ms) * 1000)
#endif



#define TRUE 1
#define FALSE 0


#define MAX_PATH FILENAME_MAX
#endif

// for enums
#ifdef __GNUC__
	#define ENUMBF(type) __extension__ enum type
#else
	#define ENUMBF(type) ZWBYTE
#endif

#if defined(__ESP32__)
typedef uint32_t ZTIME_T;
typedef struct ZIoPortSettings_s
{
    ZWWORD rx_buffer_size : 12;
    ZWBYTE uart_num : 3;
    ZWBYTE tx_pin : 6;
    ZWBYTE rx_pin : 6;
} ZIoPortSettings_t;
#else
typedef time_t ZTIME_T;
#define ZIoPortSettings_t ZWCHAR *
#endif

#include "ZThread.h"

#ifdef __cplusplus
extern "C" {
#endif

ZWEXPORT_PRIVATE ZWSTR _zerrors_sys_last_err_string_get(void);
ZWEXPORT_PRIVATE void _zerrors_sys_last_err_string_free(const ZWSTR e);

#ifdef __cplusplus
}
#endif

#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif

ZWEXPORT const char *zstrerror(ZWError err);

static inline void _zinit_null_ptr_after_memset_clear(void *ptr)
{
    if (NULL != (void *)0)
    {
        ptr = NULL;
    }
    (void)ptr;
};

#include "ZMutex.h"

typedef ZWDWORD64 ZWTIMEMS;

#endif
